#pragma once

#ifndef _OGL_SCHENE
#define _OGL_SCHENE

#include "OGLDataObjects.h"

#include <gl/glew.h>
#include <gl/GLU.h>
#include <gl/glfw.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/matrix_operation.hpp>

#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <math.h>


// OGL Function Return Values
#define OGL_SUCCESS 0
#define OGL_WARNING 1
#define OGL_ERROR 2
#define OGL_FATAL 3

// Default Startup Values
#define VAOID_NAME_MAX_LENGTH 720
#define WINDOW_NAME_MAX_LENGTH 50
#define MAX_FPS_COUNTER_CHARS 8
#define MAX_SHADERS_PER_PROGRAM 10
#define DEFAULT_WINDOW_WIDTH 1280
#define DEFAULT_WINDOW_HEIGHT 720
#define DEFAULT_OPENGL_VERSION_MAJOR 3
#define DEFAULT_OPENGL_VERSION_MINOR 3
#define DEFAULT_FSAA_SAMPLES 0

// OpenGL Schene and Global Data Class
class OGLSchene
{
private :

	glm::vec3 defaultCamLoc, defaultCamDir, defaultCamOrient;
	float defaultFov, defaultAspectRatio, defaultRangeMin, defaultRangeMax;

	std::string windowName;
	int windowHeight, windowWidth, oglVerMajor, oglVerMinor, fsaaSamples;
	glm::mat4 viewMat, projectionMat, pvMat;
	glm::vec3 camLoc, camDir, camOrient;
	GLfloat fov, aspectRatio, rangeMin, rangeMax;	
	bool pvMatChange;

	std::map<std::string, GLuint> vaoIDs;
	std::map<std::string, GLuint> programIDs;
	std::map<std::string, GLuint> matrixIDs;
	std::map<std::string, OGLDataObject *> bufferObjects;
	std::map<std::string, int>	bufferObjectCount;

	void setDefaults(void);
	int initWindow(void);
	int destroyVBOs(void);
	int destroyShaders(void);

public :
	
	// constructors and destructors
	OGLSchene();
	OGLSchene(int OpenGLVersionMajor, int OpenGLVersionMinor);
	OGLSchene(int OpenGLVersionMajor, int OpenGLVersionMinor, int fsaaSamples);

	~OGLSchene();

	// Initialization and Object Creation
	int InitializeSchene(std::string windowName);
	int InitializeSchene(
		std::string windowName,
		int windowWidth,
		int windowHeight
	);
	
	int AddObjectBufferData(std::string bufferName, OGLDataObject *bufferObj, int count);
	int AddVAOID(std::string vaoName, GLuint vaoID);
	int AddProgramID(std::string programName, GLuint programID);
	int AddMatrixID(std::string matrixName, GLuint matrixID);

	void UpdateWindowDims(int windowWidth, int windowHeight, bool forceResize);

	// Object Access
	glm::mat4 GetPVMat(void);
	int GetVAOID(std::string vaoName, GLuint *vaoID_out);
	int GetProgramID(std::string programName, GLuint *pID_out);
	int GetMatrixID(std::string matrixName, GLuint *mID_out);
	int GetObjectBufferData(std::string bufferName, OGLDataObject **obj_out, int *count_out);
	
	std::string GetWindowName(void);
	void GetWindowDims(int *windowWidth, int *windowHeight);

	void GetCamera(
		glm::vec3 *camLoc_out,	 // position of camera in the world
		glm::vec3 *camDir_out,	 // direction towards which camera is facing
		glm::vec3 *camOrient_out // orientation of camera lens
	);
	void GetScheneParams(
		GLfloat *fov_out,			// Field of view
		GLfloat *aspectRatio_out,	// Aspect ratio
		GLfloat *rangeMin_out,		// View range minimum
		GLfloat *rangeMax_out		// View range maximum
	);

	// Schene Control
	int SetCamera(void);	// Sets Camera to default values
	int SetCamera(
		glm::vec3 camLoc,	 // position of camera in the world
		glm::vec3 camDir,	 // direction towards which camera is facing
		glm::vec3 camOrient // orientation of camera lens
	);
	int SetScheneParams(void);	// Sets Schene Parameters to default values
	int SetScheneParams(
		GLfloat fov,			// Field of view
		GLfloat aspectRatio,	// Aspect ratio
		GLfloat rangeMin,		// View range minimum
		GLfloat rangeMax		// View range maximum
	);

	// Cleanup and Object Destruction
	int RemoveObjectBufferData(std::string bufferName);
	int RemoveVAO(std::string vaoName);
	int RemoveProgram(std::string programName);
	int RemoveMatrix(std::string matrixName);

	// TODO : Have this function do what OGLCleanup() was originally doing
	int DestroyScheneData(void);
};

#endif